/*
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you under the Apache License, Version 2.0 (the
 *  "License"); you may not use this file except in compliance
 *  with the License.  You may obtain a copy of the License at
 *  
 *    http://www.apache.org/licenses/LICENSE-2.0
 *  
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License. 
 *  
 */
package org.apache.directory.studio.proxy;


import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import org.apache.directory.ldap.client.api.LdapConnection;
import org.apache.directory.server.protocol.shared.transport.TcpTransport;
import org.apache.directory.shared.ldap.message.internal.InternalAbandonRequest;
import org.apache.directory.shared.ldap.message.internal.InternalAddRequest;
import org.apache.directory.shared.ldap.message.internal.InternalBindRequest;
import org.apache.directory.shared.ldap.message.internal.InternalCompareRequest;
import org.apache.directory.shared.ldap.message.internal.InternalDeleteRequest;
import org.apache.directory.shared.ldap.message.internal.InternalExtendedRequest;
import org.apache.directory.shared.ldap.message.internal.InternalModifyDnRequest;
import org.apache.directory.shared.ldap.message.internal.InternalModifyRequest;
import org.apache.directory.shared.ldap.message.internal.InternalSearchRequest;
import org.apache.directory.shared.ldap.message.internal.InternalUnbindRequest;
import org.apache.directory.studio.proxy.handlers.AbandonHandler;
import org.apache.directory.studio.proxy.handlers.AddHandler;
import org.apache.directory.studio.proxy.handlers.BindHandler;
import org.apache.directory.studio.proxy.handlers.CompareHandler;
import org.apache.directory.studio.proxy.handlers.DeleteHandler;
import org.apache.directory.studio.proxy.handlers.ExtendedHandler;
import org.apache.directory.studio.proxy.handlers.ModifyDnHandler;
import org.apache.directory.studio.proxy.handlers.ModifyHandler;
import org.apache.directory.studio.proxy.handlers.SearchHandler;
import org.apache.directory.studio.proxy.handlers.UnbindHandler;
import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.transport.socket.SocketAcceptor;


/**
 * This class implements a simple LDAP Network Server.
 *
 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
 */
public class LdapNetworkServer
{
    /**
     * The port on which the server should run.
     */
    private int port = 10389;

    /**
     * The Map storing the opened Ldap Connection for each session.
     */
    private Map<IoSession, LdapConnection> connectionsMap = new HashMap<IoSession, LdapConnection>();


    /**
     * Starts the server.
     */
    public void start()
    {
        DefaultIoFilterChainBuilder chain = new DefaultIoFilterChainBuilder();

        chain.addLast( "codec", new ProtocolCodecFilter( new LdapProtocolCodecFactory() ) );

        TcpTransport transport = new TcpTransport( port );

        SocketAcceptor acceptor = transport.getAcceptor();

        // Now, configure the acceptor
        // Disable the disconnection of the clients on unbind
        acceptor.setCloseOnDeactivation( false );

        // Allow the port to be reused even if the socket is in TIME_WAIT state
        acceptor.setReuseAddress( true );

        // No Nagle's algorithm
        acceptor.getSessionConfig().setTcpNoDelay( true );

        // Inject the chain
        acceptor.setFilterChainBuilder( chain );

        LdapProtocolHandler handler = new LdapProtocolHandler();
        handler.addReceivedMessageHandler( InternalAbandonRequest.class, new AbandonHandler() );
        handler.addReceivedMessageHandler( InternalAddRequest.class, new AddHandler() );
        handler.addReceivedMessageHandler( InternalBindRequest.class, new BindHandler() );
        handler.addReceivedMessageHandler( InternalCompareRequest.class, new CompareHandler() );
        handler.addReceivedMessageHandler( InternalDeleteRequest.class, new DeleteHandler() );
        handler.addReceivedMessageHandler( InternalExtendedRequest.class, new ExtendedHandler() );
        handler.addReceivedMessageHandler( InternalModifyDnRequest.class, new ModifyDnHandler() );
        handler.addReceivedMessageHandler( InternalModifyRequest.class, new ModifyHandler() );
        handler.addReceivedMessageHandler( InternalSearchRequest.class, new SearchHandler() );
        handler.addReceivedMessageHandler( InternalUnbindRequest.class, new UnbindHandler() );

        // Inject the protocol handler
        acceptor.setHandler( handler );

        // Bind to the configured address
        try
        {
            acceptor.bind();
        }
        catch ( IOException e )
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        System.out.println( "Server started." );
    }


    /**
     * Stops the server
     */
    public void stop()
    {
        // TODO Auto-generated method stub

    }


    /**
     * Gets the port.
     *
     * @return
     *      the port
     */
    public int getPort()
    {
        return port;
    }


    /**
     * Sets the port.
     *
     * @param port
     *      the port
     */
    public void setPort( int port )
    {
        this.port = port;
    }


    public LdapConnection getConnection( IoSession session )
    {
        return connectionsMap.get( session );
    }


    public void setConnection( IoSession session, LdapConnection connection )
    {
        connectionsMap.put( session, connection );
    }
}
